package com.ganzi.ganziserver.shiro.filter;

import java.io.Serializable;
import java.util.Deque;
import java.util.LinkedList;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;

import com.ganzi.ganziserver.core.User;
import com.ganzi.ganziserver.shiro.redis.OtherCache.UserCache;
import com.ganzi.ganziserver.shiro.redis.RedisManager;
import com.ganzi.ganziserver.shiro.redis.ShiroSessionManager;
import org.apache.commons.lang3.StringUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.DefaultSessionKey;
import org.apache.shiro.session.mgt.SessionManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.filter.AccessControlFilter;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.resource.ResourceUrlProvider;

/**
 shiro 自定义filter 实现 并发登录控制
 */
public class KickoutSessionControlFilter  extends AccessControlFilter{
    private static Logger logger = LoggerFactory.getLogger(KickoutSessionControlFilter.class);

    @Autowired
    private ResourceUrlProvider resourceUrlProvider;
    @Autowired
    UserCache cache;



    /** 踢出后到的地址 */
    private String kickoutUrl;

    /**  踢出之前登录的/之后登录的用户 默认踢出之前登录的用户 */
    private boolean kickoutAfter = false;

    /**  同一个帐号最大会话数 默认1 */
    private int maxSession = 1;
    private SessionManager sessionManager;

    private RedisManager redisManager;

    public static final String DEFAULT_KICKOUT_CACHE_KEY_PREFIX = "shiro:cache:kickout:";
    private String keyPrefix = DEFAULT_KICKOUT_CACHE_KEY_PREFIX;

    public void setKickoutUrl(String kickoutUrl) {
        this.kickoutUrl = kickoutUrl;
    }

    public void setKickoutAfter(boolean kickoutAfter) {
        this.kickoutAfter = kickoutAfter;
    }

    public void setMaxSession(int maxSession) {
        this.maxSession = maxSession;
    }

    public void setSessionManager(SessionManager sessionManager) {
        this.sessionManager = sessionManager;
    }

    public void setRedisManager(RedisManager redisManager) {
        this.redisManager = redisManager;
    }

    public String getKeyPrefix() {
        return keyPrefix;
    }

    public void setKeyPrefix(String keyPrefix) {
        this.keyPrefix = keyPrefix;
    }

    private String getRedisKickoutKey(String username) {
        return this.keyPrefix + username;
    }


    @Override
    protected boolean isAccessAllowed(ServletRequest request, ServletResponse response, Object mappedValue) throws Exception {
        return false;
    }

    @Override
    protected boolean onAccessDenied(ServletRequest request, ServletResponse response) throws Exception {
        Subject subject = getSubject(request, response);
        if(!subject.isAuthenticated() && !subject.isRemembered()) {
            //如果没有登录，直接进行之后的流程
            return true;
        }
        //如果有登录,判断是否访问的为静态资源，如果是游客允许访问的静态资源,直接返回true
        HttpServletRequest httpServletRequest = (HttpServletRequest)request;
        String path = httpServletRequest.getServletPath();
        // 如果是静态文件，则返回true
        if (isStaticFile(path)){
            return true;
        }
        Session session = subject.getSession();
        String username = (String) subject.getPrincipal();
        Serializable sessionId = session.getId();

        Deque<Serializable> deque = (Deque<Serializable>) redisManager.get(getRedisKickoutKey(username));
        if(deque == null || deque.size()==0) {
            deque = new LinkedList<Serializable>();
        }
        if(!deque.contains(sessionId) && session.getAttribute("kickout") == null) {
            deque.push(sessionId);
        }
        while(deque.size() > maxSession) {
            Serializable kickoutSessionId = null;
            if(kickoutAfter) {
                kickoutSessionId = deque.removeFirst();
            } else {
                kickoutSessionId = deque.removeLast();
            }
            try {
                Session kickoutSession = sessionManager.getSession(new DefaultSessionKey(kickoutSessionId));
                if(kickoutSession != null) {
                    kickoutSession.setAttribute("kickout", true);//标记清楚
                }
            } catch (UnknownSessionException e) {
                redisManager.set(getRedisKickoutKey(username), deque);
                return true;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        redisManager.set(getRedisKickoutKey(username), deque);
        //处理标记清除
        if (session.getAttribute("kickout") != null) {
            try {
                cache.removeCacheUser((String)session.getId());//删除用户缓存
                subject.logout();
            } catch (Exception e) {
                logger.error(e.getMessage());
            }
            String id = WebUtils.toHttp(request).getHeader(ShiroSessionManager.AUTHORIZATION);
            if(StringUtils.isNotBlank(id)){
                WebUtils.toHttp(response).sendError(4002);
                return false;
            }
        }
        return true;
    }

    private boolean isStaticFile(String path) {
        String staticUri = resourceUrlProvider.getForLookupPath(path);
        return staticUri != null;
    }

}
